#include "avrupdate.h"

#if WITHNETWORKSUPPORT
#include "http_fetcher.h"
#endif

#define STARTAPP       0x01
#define WRITEPAGE      0x02
#define GETVERSION     0x03
#define SETVERSION     0x04
#define STOPPROGMODE   0x05

// globals
static IONotificationPortRef	gNotifyPort;
static io_iterator_t		gRawAddedIter;
static char			gBuffer[64];

#ifdef COMMENT_OUT
void avrupdate_flash_bin(struct usb_dev_handle* usb_handle,char *file)
{
    char buf[64];
    char cmd[64];

    FILE *fd;


    // get page size
    // get flash size
 
    // send every page in a usb package
    // 1. header ( action, page number)
    // 2. data   

    // if you have got a controller with blocksize greater than 64
    // work with the easyavr like you have an 64 page size flash
    // the controller manages the rest

    //printf("send %s to easyavr\r\n",file);

    int page=0;
    int offset=0;

   // open bin file
    fd = fopen(file, "r+b");
    if(!fd) {
      fprintf(stderr, "Unable to open file %s, ignoring.\n", file);
    }

    while(!feof(fd))
    {
      buf[offset]=fgetc(fd);

      offset++;
      if(offset == 64)
      {
          //printf("send package\n");
          // command message
          cmd[0]=WRITEPAGE;
          cmd[1]=(char)page; // page number
          usb_bulk_write(usb_handle,2,cmd,64,100);

          // data message 
          usb_bulk_write(usb_handle,2,buf,64,100);
          offset = 0;
          page++;
      }
    }
    if(offset > 0)
    {
      //printf("rest\n");
      // command message
      cmd[0]=WRITEPAGE;
      cmd[1]=(char)page; // page number
      usb_bulk_write(usb_handle,2,cmd,64,100);

      // data message 
      usb_bulk_write(usb_handle,2,buf,64,100);
    }   
}


void avrupdate_startapp(struct usb_dev_handle* usb_handle)
{
    char buf[64];
    char *ptr = buf;

//     buf[0]=STOPPROGMODE;
//     usb_bulk_write(usb_handle,2,ptr,64,100);

     buf[0]=STARTAPP;
     usb_bulk_write(usb_handle,2,ptr,64,100);
}
#endif

int DeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t		kr;
    io_service_t		usbDevice;
    IOCFPlugInInterface 	**plugInInterface=NULL;
    IOUSBDeviceInterface 	**dev=NULL;
    HRESULT 			res;
    SInt32 			score;
    UInt16			vendor;
    UInt16			product;
    UInt16			bcdDevice;
    
    while ( (usbDevice = IOIteratorNext(iterator)) )
    {
        //printf("Raw device added.\n");
       
        kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
        kr = IOObjectRelease(usbDevice);				// done with the device object now that I have the plugin
        if ((kIOReturnSuccess != kr) || !plugInInterface)
        {
            printf("unable to create a plugin (%08x)\n", kr);
            continue;
        }
            
        // I have the device plugin, I need the device interface
        res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&dev);
        IODestroyPlugInInterface(plugInInterface);			// done with this
		
        if (res || !dev)
        {
            printf("couldn't create a device interface (%08x)\n", (int) res);
            continue;
        }
        // technically should check these kr values
        kr = (*dev)->GetDeviceVendor(dev, &vendor);
        kr = (*dev)->GetDeviceProduct(dev, &product);
        kr = (*dev)->GetDeviceReleaseNumber(dev, &bcdDevice);
		switch (vendor)
		{
            case 1003:
              if(product==0x2104)
                return AVRISPMKII;
            break;
            case 6017:
              if(product==0x0c62){
                if(bcdDevice==AVRUPDATE)
                  return AVRUPDATE;
                if(bcdDevice==0x0200)
                  return USBPROG;
                if(bcdDevice==BLINKDEMO)
                  return BLINKDEMO;
			   }
		   break;
		   case 1741:
			  if(product == 289)
			     return KEYSPAN;
		   break;
		}
    }
	return -1;
}

int avrupdate_find_usbdevice()
{
	mach_port_t 		masterPort;
    CFMutableDictionaryRef 	matchingDict;
    kern_return_t		kr;
	
	// first create a master_port for my task
    kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
    if (kr || !masterPort)
    {
        printf("ERR: Couldn't create a master IOKit Port(%08x)\n", kr);
        return -1;
    }

    // Set up the matching criteria for the devices we're interested in
    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);	// Interested in instances of class IOUSBDevice and its subclasses
    if (!matchingDict)
    {
        printf("Can't create a USB matching dictionary\n");
        mach_port_deallocate(mach_task_self(), masterPort);
        return -1;
    }

	
	// Create a notification port and add its run loop event source to our run loop
    // This is how async notifications get set up.
    gNotifyPort = IONotificationPortCreate(masterPort);
    
    // Retain additional references because we use this same dictionary with four calls to 
    // IOServiceAddMatchingNotification, each of which consumes one reference.
    matchingDict = (CFMutableDictionaryRef) CFRetain( matchingDict ); 
    
    // Now set up notification, one to be called when a device is first matched by I/O Kit
	// I should be able to do this with the matching notifier but I don't know how yet
    kr = IOServiceAddMatchingNotification(  gNotifyPort,
                                            kIOFirstMatchNotification,
                                            matchingDict,
                                            RawDeviceAdded,
                                            NULL,
                                            &gRawAddedIter );
										
    return DeviceAdded(NULL, gRawAddedIter);	// Iterate once to get already-present devices and
                                                // arm the notification
}

void avrupdate_start_with_vendor_request(short vendorid, short productid)
{
  struct usb_bus *busses;

  //usb_set_debug(2);
  usb_init();
  usb_find_busses();
  usb_find_devices();

  busses = usb_get_busses();

   struct usb_dev_handle* usb_handle;
  struct usb_bus *bus;

  //if(avrupdate_find_usbdevice()==USBPROG)
  //  return;

  unsigned char send_data=0xff;

  for (bus = busses; bus; bus = bus->next) {
    struct usb_device *dev;

    for (dev = bus->devices; dev; dev = dev->next){
      if (dev->descriptor.idVendor == vendorid){
         int i,stat;
        //printf("found: %i\n",dev->descriptor.idVendor);
         usb_handle = usb_open(dev);
         usb_set_configuration (usb_handle,1);
        usb_claim_interface(usb_handle,0);
        usb_set_altinterface(usb_handle,0);

        int timeout=6;

        while(usb_control_msg(usb_handle, 0xC0, 0x01, 0, 0, NULL,8, 1000)<0){
          timeout--;
          if(timeout==0)
            break;
        }
        usb_close(usb_handle);
       }
     }  
   }
}
